﻿//Copyright (c) 2010 Christopher E. S. King (http://chriskingconsulting.com)
//
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
// (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;

using King.Extensions;
using King.Extensions.Collections;

namespace King.Reflection {

    public class AssemblyNameComparer {

        public static readonly IEqualityComparer<AssemblyName> Value;

        static AssemblyNameComparer() {
            Value = EqualityComparer.Construct<AssemblyName>(
                (l, r) => l.FullName.ToLower().Equals(r.FullName.ToLower()), 
                l => l.FullName.ToLower().GetHashCode());
        }
    }

    public class DisposableAppDomain : IDisposable {
        private AppDomain m_appDomain;

        public DisposableAppDomain(AppDomain appDomain) {
            m_appDomain = appDomain;
        }

        public AppDomain AppDomain {
            get { return m_appDomain; }
        }

        public T Activate<T>() {
            return (T)m_appDomain.Activate(typeof(T));
        }

        public void Dispose() {
            Dispose(false);
        }
        private void Dispose(bool finalizing) {
            if (!finalizing)
                GC.SuppressFinalize(this);
            AppDomain.Unload(m_appDomain);
        }
        ~DisposableAppDomain() {
            Dispose(true);
        }
    }
    public static class StringExtensions {

        /// <summary>
        /// Deserialize the string as an enum of type T. 
        /// </summary>
        /// <typeparam name="T">Type type of the enum.</typeparam>
        /// <param name="source">The serialized value of the enum. Must be non null unless T is nullable.</param>
        /// <returns>The deserilaized value of the string as an Enum of type T or null if T is nullable and source is empty or null.</returns>
        public static T ToEnum<T>(this string source) {
            Contract.Requires(typeof(T).IsEnum || typeof(T).IsNullableSubclass(typeof(Enum)));

            if (string.IsNullOrEmpty(source) && typeof(T).IsNullable())
                return default(T);

            return (T)Enum.Parse(typeof(T).MakeNotNullable(), source);
        }

        /// <summary>
        /// Create an AppDomain with using this as the ApplicationBase. AppDomains usually created
        /// to generate another loader context which makes the ApplicationBase the most important
        /// parameter passed during AppDomain creation.
        /// </summary>
        /// <param name="applicationBase">The ApplicationBase of the new AppDomain.</param>
        /// <param name="name">The name of the new AppDomain.</param>
        /// <param name="shadowCopy">If shadow copy is enabled.</param>
        /// <returns>An AppDomain wrapped in DisposableAppDomain.</returns>
        public static DisposableAppDomain CreateAppDomain(this string applicationBase,
            string name = "AppDomain",
            bool shadowCopy = false) {

            var result = AppDomain.CreateDomain(name,
                securityInfo: AppDomain.CurrentDomain.Evidence,
                info: new AppDomainSetup() {
                    ApplicationBase = applicationBase,
                    ShadowCopyFiles = shadowCopy ? "true" : null,
                });

            return new DisposableAppDomain(result);
        }
    }

    public static class AppDomainExtensions {

        /// <summary>
        /// Activate an instance of a type T in the AppDomain and return a proxy to this AppDomain.
        /// </summary>
        /// <typeparam name="T">The type to activate in the AppDomain.</typeparam>
        /// <param name="appDomain">The AppDomain in which the type should be activated.</param>
        /// <returns>A proxy to the type.</returns>
        public static T Activate<T>(this AppDomain appDomain) {
            return (T)appDomain.Activate(typeof(T));
        }

        /// <summary>
        /// Activate an instance of type in the AppDomain and return a proxy to this AppDomain.
        /// </summary>
        /// <param name="appDomain">The AppDomain in which the type should be activated.</param>
        /// <param name="type">The type to create a in the AppDomain.</param>
        /// <returns>A proxy to the type T.</returns>
        public static object Activate(this AppDomain appDomain, Type type) {
            return appDomain.CreateInstanceAndUnwrap(
                type.Assembly.FullName,
                type.FullName
            );
        }
    }

    public static class CustomAttributeExtensions {

        public static bool IsDeclared(this ICustomAttributeProvider source, Type attributeType) {
            return source.IsDefined(attributeType, false);
        }
        public static bool IsDeclared<T>(this ICustomAttributeProvider source) {
            return source.IsDefined(typeof(T), false);
        }

        public static bool IsDefined(this ICustomAttributeProvider source, Type attributeType) {
            return source.IsDefined(attributeType, true);
        }
        public static bool IsDefined<T>(this ICustomAttributeProvider source) {
            return source.IsDefined(typeof(T), true);
        }
        public static IEnumerable<object> GetAttributes(this ICustomAttributeProvider source) {
            return source.GetAttributes(typeof(Attribute));
        }
        public static IEnumerable<object> GetAttributes(this ICustomAttributeProvider source, Type type) {
            IEnumerable<object> attributes = source.GetCustomAttributes(type, true);

            if (attributes == null)
                return Enumerable.Empty<object>();

            return attributes;
        }
        public static IEnumerable<T> GetAttributes<T>(this ICustomAttributeProvider source) {
            return source.GetAttributes(typeof(T)).Cast<T>();
        }
        public static object GetAttribute(this ICustomAttributeProvider source, Type type) {
            return source.GetAttributes(type).SingleOrDefault();
        }
        public static T GetAttribute<T>(this ICustomAttributeProvider source) {
            return source.GetAttributes<T>().SingleOrDefault();
        }
    }

    public static partial class TypeExtensions {

        internal static BindingFlags GetBindingFlags(
            bool? isPublic = null, bool? isNonPublic = null, 
            bool? isInstance = null, bool? isStatic = null,
            bool? flattenHierarchy = null, bool? declaredOnly = null,
            bool? useExactBinding = null, bool? isCaseInsensitive = null) {

            if (isPublic == null && isNonPublic == null)
                isPublic = isNonPublic = true;

            if (isStatic == null && isInstance == null)
                isStatic = isInstance = true;

            var bindingFlags = new[] { 
                new { BindingFlag = BindingFlags.Public,            Set = isPublic },
                new { BindingFlag = BindingFlags.NonPublic,         Set = isNonPublic},
                new { BindingFlag = BindingFlags.Static,            Set = isStatic },
                new { BindingFlag = BindingFlags.Instance,          Set = isInstance },
                new { BindingFlag = BindingFlags.DeclaredOnly,      Set = declaredOnly },
                new { BindingFlag = BindingFlags.FlattenHierarchy,  Set = flattenHierarchy },
                new { BindingFlag = BindingFlags.ExactBinding,      Set = useExactBinding },
                new { BindingFlag = BindingFlags.IgnoreCase,        Set = isCaseInsensitive },
            }.Aggregate(default(BindingFlags), (a, o) => a |= (o.Set != true ? a : o.BindingFlag));

            return bindingFlags;
        }

        /// <summary>
        ///  Determines whether the class represented by the current Type derives
        ///  from or is the class represented by the specified Type.
        /// </summary>
        /// <param name="source">The current type.</param>
        /// <param name="target">The Type to compare with the current Type.</param>
        /// <returns>
        /// true if the Type represented by the target parameter and the current Type represent
        /// classes, and the class represented by the current Type derives from or is the class
        /// represented by target; otherwise, false.
        /// </returns>
        public static bool IsSubclassOfOrIs(this Type source, Type target) {
            Contract.Requires(source != null);
            Contract.Requires(target != null);

            return source == target || source.IsSubclassOf(target);
        }

        public static IEnumerable<T> Get<T>(this Type source, 
            string name = "*", object types = null,
            bool? isPublic = null, bool? isNonPublic = null, 
            bool? isInstance = null, bool? isStatic = null,
            bool? flattenHierarchy = null, bool? declaredOnly = null,
            bool? useExactBinding = false, bool? isPropertyOrField = null,
            bool? isCaseInsensitive = null) where T : MemberInfo {

            Contract.Requires(isPropertyOrField != true || typeof(T) == typeof(MemberInfo));
            Contract.Requires(types == null || types is IEnumerable<Type>);
            Contract.Requires(types == null || typeof(T).IsSubclassOfOrIs(typeof(MethodBase)));

            var bindingFlags = GetBindingFlags(isPublic, isNonPublic, 
                isInstance, isStatic, flattenHierarchy, declaredOnly, 
                useExactBinding, isCaseInsensitive);

            var memberTypes = isPropertyOrField == true ? MemberTypes.Property | MemberTypes.Field : new[] {
                new { MemberType = MemberTypes.Constructor,         Type = typeof(ConstructorInfo) }, 
                new { MemberType = MemberTypes.Method,              Type = typeof(MethodInfo) }, 
                new { MemberType = MemberTypes.Property,            Type = typeof(PropertyInfo) }, 
                new { MemberType = MemberTypes.Event,               Type = typeof(EventInfo) }, 
                new { MemberType = MemberTypes.Field,               Type = typeof(FieldInfo) }, 
            }.Aggregate(default(MemberTypes), (a, o) => a |= (o.Type.IsSubclassOfOrIs(typeof(T)) ? o.MemberType : a));

            var canidates = source.GetMember(name, memberTypes, bindingFlags);

            if (types == null)
                return canidates.Cast<T>();

            var parameterTypes = types.ToEnumerable<Type>().ToArray();
            object result = canidates.First() is PropertyInfo ?
                // bind to property
                (object)Type.DefaultBinder.SelectProperty(
                    bindingFlags, canidates.Cast<PropertyInfo>().ToArray(), null, parameterTypes, null) :

                // bind to method
                (object)Type.DefaultBinder.SelectMethod(
                    bindingFlags, canidates.Cast<MethodBase>().ToArray(), parameterTypes, null);

            if (result == null)
                return Enumerable.Empty<T>();

            return new T[] { (T)result };
        }

        /// <summary>
        /// Test if the type is an instantiation of the generic type definition.
        /// </summary>
        /// <param name="source">The type in question.</param>
        /// <param name="genericTypeDefinition">The generic type definition.</param>
        /// <returns>True if the type is an instantiation of the generic type definition.</returns>
        public static bool IsInstantiationOf(this Type source, Type genericTypeDefinition) {
            Contract.Requires(source != null);
            Contract.Requires(genericTypeDefinition != null);
            Contract.Requires(genericTypeDefinition.IsGenericTypeDefinition);

            if (!source.IsGenericType)
                return false;

            if (source.IsGenericTypeDefinition)
                return false;

            return source.GetGenericTypeDefinition() == genericTypeDefinition;
        }

        /// <summary>
        /// Test if the type can be assinged a null value; is a class, interface, pointers or 
        /// nullable type.
        /// </summary>
        /// <param name="source">The type to test.</param>
        /// <returns>True if null can be assigned to a location of this type.</returns>
        public static bool CanBeNull(this Type source) {
            Contract.Requires(source != null);

            if (source.IsClass)
                return true;

            if (source.IsInterface)
                return true;

            if (source.IsPointer)
                return true;

            if (source.IsInstantiationOf(typeof(Nullable<>)))
                return true;

            return false;
        }

        /// <summary>
        /// Creates an instance of the specified type using the parameterless constructor regardless of its visibility.
        /// </summary>
        /// <param name="type">The type of object to create.</param>
        /// <returns>A reference to the newly created object.</returns>
        public static object Activate(this Type type) {
            Contract.Requires(type != null);

            try {
                return Activator.CreateInstance(type, true);

            } catch (TargetInvocationException e) {
                throw e.InnerException;
            }
        }

        /// <summary>
        /// Creates an instance of the specified type using the constructor that best matches the specified 
        /// parameters. Binding is done against all constructors regardless of visibility using the default binder 
        /// and the invariant culture.
        /// </summary>
        /// <param name="type">The type of object to create.</param>
        /// <param name="arguments"> An array of arguments that match in number, order, and type the parameters
        ///  of the constructor to invoke. If args is an empty array or null, the constructor
        ///  that takes no parameters (the default constructor) is invoked.</param>
        /// <returns>A reference to the newly created object.</returns>
        public static object Activate(this Type type, IEnumerable<object> arguments) {
            Contract.Requires(type != null);
            Contract.Requires(arguments != null);

            return Activate(type, arguments.ToArray());
        }

        /// <summary>
        /// Creates an instance of the specified type using the constructor that best matches the specified 
        /// parameters. Binding is done against all constructors regardless of visibility using the default binder 
        /// and the invariant culture.
        /// </summary>
        /// <param name="type">The type of object to create.</param>
        /// <param name="arguments"> An array of arguments that match in number, order, and type the parameters
        /// of the constructor to invoke. If args is an empty array or null, the constructor
        /// that takes no parameters (the default constructor) is invoked.</param>
        /// <returns>A reference to the newly created object.</returns>
        public static object Activate(this Type type, params object[] arguments) {
            Contract.Requires(type != null);

            try {
                return Activator.CreateInstance(
                    type,
                    BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
                    Type.DefaultBinder,
                    arguments,
                    CultureInfo.InvariantCulture,
                    null);

            } catch (TargetInvocationException e) {
                throw e.InnerException;
            }
        }

        /// <summary>
        /// Return whether the type is an instance of Nullable[[T]]
        /// </summary>
        /// <param name="type">The type to test.</param>
        /// <returns>True if the type is an instance of Nullable[[T]].</returns>
        public static bool IsNullable(this Type type) {
            Contract.Requires(type != null);
            return type.IsInstantiationOf(typeof(Nullable<>));
        }

        /// <summary>
        /// Return whether the type is an instance of Nullable[T] 
        /// where T is a subclass of baseClass.
        /// </summary>
        /// <param name="type">The type to test.</param>
        /// <param name="baseClass">The type of which T must be a subclass inorder to return true.</param>
        /// <returns>True if the type is an instance of Nullable[T] wher T is a subclass of baseClass.</returns>
        public static bool IsNullableSubclass(this Type type, Type baseClass) {
            Contract.Requires(type != null);
            Contract.Requires(baseClass != null);

            if (!type.IsInstantiationOf(typeof(Nullable<>)))
                return false;

            return type.GetGenericArguments().Single().IsSubclassOf(baseClass);
        }

        /// <summary>
        /// Makes a nullable version of this Type. If the type is already an instance of nullable then it is returned.
        /// </summary>
        /// <param name="type">The type to make nullable.</param>
        /// <returns>A nullable version of this type or the type itself if its already an instance of nullable.</returns>
        public static Type MakeNullable(this Type type) {
            Contract.Requires(type != null);

            if (type.IsNullable())
                return type;

            return typeof(Nullable<>).MakeGenericType(type);
        }

        /// <summary>
        /// Return a non-nullable version of this Type. If the type is non nullable then this type is returned.
        /// </summary>
        /// <param name="type">The type to make non-nullable.</param>
        /// <returns>A non-nullable version of this Type or if this Type is not nullable return this Type.</returns>
        public static Type MakeNotNullable(this Type type) {
            Contract.Requires(type != null);

            if (type.IsInstantiationOf(typeof(Nullable<>)))
                return type.GetGenericArguments().Single();

            return type;
        }

        /// <summary>
        /// Return the generic argument of this Type. Type must have a single generic argument.
        /// </summary>
        /// <param name="type">A type with a signle generic argument.</param>
        /// <returns>The single generic argument of this Type.</returns>
        public static Type GetGenericArgument(this Type type) {
            Contract.Requires(type != null);
            Contract.Requires(type.IsGenericType && type.GetGenericArguments().Count() == 1);

            return type.GetGenericArguments().Single();
        }

        /// <summary>
        /// Returns whether a method of the given name and parameter types exists on type and is an override.
        /// </summary>
        /// <param name="type">The type on which to search for the method.</param>
        /// <param name="name">The name of the method.</param>
        /// <param name="parameterTypes">The exact types of the parameters of the method.</param>
        /// <returns></returns>
        public static bool Overrides(this Type type, string name, params Type[] parameterTypes) {
            Contract.Requires(type != null);
            Contract.Requires(name != null);
            
            var methodInfos = type.Get<MethodInfo>(name, parameterTypes, useExactBinding: true);
            if (methodInfos.None())
                throw new ArgumentException(
                    "No method " + name + " on " + type + " could be bound using the supplied parameter types.");

            return methodInfos.Single().IsOverride();
        }

        public static object Invoke(this Type type, string name,
            object target = null,
            IEnumerable<object> arguments = null,
            IEnumerable<string> parameterNames = null,
            bool? isPublic = null, bool? isNonPublic = null,
            bool? isInstance = null, bool? isStatic = null,
            bool? flattenHierarchy = null, bool? declaredOnly = null,
            bool? useExactBinding = null, bool? isCaseInsensitive = null) {

            var bindingFlags = TypeExtensions.GetBindingFlags(
                isPublic, isNonPublic, isInstance, isStatic, 
                flattenHierarchy, declaredOnly, useExactBinding, isCaseInsensitive);

            bindingFlags |= BindingFlags.OptionalParamBinding;

            if (name == ".ctor")
                bindingFlags |= BindingFlags.CreateInstance;
            else
                bindingFlags |= BindingFlags.InvokeMethod;

            object[] argumentArray = null;
            if (arguments != null)
                argumentArray = arguments.ToArray();

            string[] parameterNameArray = null;
            if (parameterNames != null)
                parameterNameArray = parameterNames.ToArray();

            var binder = Type.DefaultBinder;
            var culture = CultureInfo.InvariantCulture;

            var result = type.InvokeMember(name, bindingFlags, binder, target, argumentArray, null, culture, parameterNameArray);
            return result;
        }
    }

    public static class Primitive {

        /// <summary>
        /// Invoke the static method TryParse declared on the passed type. Type must be a primitive number format (or decimal). 
        /// Type must be sbyte, short, int, long, byte, ushort, uint, ulong, float, double, or decimal
        /// </summary>
        /// <param name="type">A primitive type.</param>
        /// <param name="value">The value to try and parse.</param>
        /// <param name="numberStyle">A numberStyle to pass if TryParse.</param>
        /// <param name="formatProvider">A IFormatProvider to pass to TryParse.</param>
        /// <param name="result">The boxed result of the parse or null if the parse fails.</param>
        /// <returns>True if the parse succeeded. False otherwise.</returns>
        public static bool TryParse(Type type, string value, NumberStyles numberStyle, IFormatProvider formatProvider, out object result) {
            Contract.Requires(type.IsPrimitive || type == typeof(decimal));
            Contract.Requires(value != null);
            Contract.Ensures(Contract.Result<bool>() == false || Contract.ValueAtReturn<object>(out result) != null);

            result = null;
            bool success = false;

            sbyte sbyteResult;
            short shortResult;
            int intResult;
            long longResult;
            byte byteResult;
            ushort ushortResult;
            uint uintResult;
            ulong ulongResult;
            float floatResult;
            double doubleResult;
            decimal decimalResult;

            if (type == typeof(int)) {
                success = int.TryParse(value, numberStyle, formatProvider, out intResult);
                result = intResult;
            } else if (type == typeof(long)) {
                success = long.TryParse(value, numberStyle, formatProvider, out longResult);
                result = longResult;

            } else if (type == typeof(sbyte)) {
                success = sbyte.TryParse(value, out sbyteResult);
                result = sbyteResult;
            } else if (type == typeof(short)) {
                success = short.TryParse(value, numberStyle, formatProvider, out shortResult);
                result = shortResult;
            } else if (type == typeof(long)) {
                success = long.TryParse(value, numberStyle, formatProvider, out longResult);
                result = longResult;

            } else if (type == typeof(byte)) {
                success = byte.TryParse(value, numberStyle, formatProvider, out byteResult);
                result = byteResult;
            } else if (type == typeof(ushort)) {
                success = ushort.TryParse(value, numberStyle, formatProvider, out ushortResult);
                result = ushortResult;
            } else if (type == typeof(uint)) {
                success = uint.TryParse(value, numberStyle, formatProvider, out uintResult);
                result = uintResult;
            } else if (type == typeof(ulong)) {
                success = ulong.TryParse(value, numberStyle, formatProvider, out ulongResult);
                result = ulongResult;

            } else if (type == typeof(float)) {
                success = float.TryParse(value, numberStyle, formatProvider, out floatResult);
                result = floatResult;
            } else if (type == typeof(double)) {
                success = double.TryParse(value, numberStyle, formatProvider, out doubleResult);
                result = doubleResult;
            } else if (type == typeof(decimal)) {
                success = decimal.TryParse(value, numberStyle, formatProvider, out decimalResult);
                result = decimalResult;
            } 

            return success;
        }
    }

    public static partial class FieldInfoExtensions {

        /// <summary>
        /// Converts a list of fields to name\value pairs.
        /// </summary>
        /// <param name="fields">The fields to convert to name\value pairs.</param>
        /// <param name="target">The object the fields are declared on or null if the fields are all static.</param>
        /// <returns>An enumeration of name\value pairs of all the field names and values.</returns>
        public static IEnumerable<KeyValuePair<object, object>> ToKeyValuePairs(this IEnumerable<FieldInfo> fields, object target = null) {

            foreach (var field in fields)
                yield return new KeyValuePair<object, object>(field.Name, field.GetValue(target));
        }
    }

    public static partial class MemberInfoExtensions {

        /// <summary>
        /// Return the value for a PropertyInfo or a FieldInfo.
        /// </summary>
        /// <param name="source">A PropertyInfo or FieldInfo.</param>
        /// <param name="target">The target to get the value from. Can be null if the MemberInfo is static.</param>
        /// <returns>The value of the PropertyInfo or FieldInfo.</returns>
        public static object GetValue(this MemberInfo source, object target = null) {
            Contract.Requires(source is PropertyInfo || source is FieldInfo);

            var propertyInfo = source as PropertyInfo;
            if (propertyInfo != null)
                return propertyInfo.GetValue(target, null);

            var fieldInfo = source as FieldInfo;
            return fieldInfo.GetValue(target);
        }

        /// <summary>
        /// Assign the value to the property or field.
        /// </summary>
        /// <param name="source">The property or field.</param>
        /// <param name="target">The instance on which the property or field is defined. Null if the property or field is static.</param>
        /// <param name="value">The value to assign to the property or field.</param>
        public static void SetValue(this MemberInfo source, object target = null, object value = null) {
            Contract.Requires(source is PropertyInfo || source is FieldInfo);

            var propertyInfo = source as PropertyInfo;
            if (propertyInfo != null) {
                propertyInfo.SetValue(target, value, null);
                return;
            }

            var fieldInfo = source as FieldInfo;
            fieldInfo.SetValue(target, value);
        }

        /// <summary>
        /// Get the property or field type of a PropertyInfo or a FieldInfo.
        /// </summary>
        /// <param name="source">Must be a PropertyInfo or FieldInfo.</param>
        /// <returns>The property or field type of source.</returns>
        public static Type GetPropertyOrFieldType(this MemberInfo source) {
            Contract.Requires(source != null);
            Contract.Requires(source is PropertyInfo || source is FieldInfo);

            var propertyInfo = source as PropertyInfo;
            if (propertyInfo != null)
                return propertyInfo.PropertyType;

            var fieldInfo = source as FieldInfo;
            return fieldInfo.FieldType;
        }

        /// <summary>
        /// Return the parameter of a method if source is a MethodBase or the index parameters
        /// of a property is source is a ParameterInfo.
        /// </summary>
        /// <param name="source">The method or property for which to get parameters.</param>
        /// <returns>The parameters of the method or property.</returns>
        public static IEnumerable<ParameterInfo> GetParameters(this MemberInfo source) {
            Contract.Requires(source is MethodBase || source is PropertyInfo);

            if (source is MethodBase)
                return ((MethodBase)source).GetParameters();

            return ((PropertyInfo)source).GetIndexParameters();
        }

        /// <summary>
        /// Test if an instance of a given type can be assigned to this member. 
        /// The member must be a field or a property.
        /// </summary>
        /// <param name="source">The field or property.</param>
        /// <param name="value">The type to assign to the location.</param>
        /// <returns>True if an instance of the type can be assigned to the member.</returns>
        public static bool IsAssignableFrom(this MemberInfo source, Type target) {
            Contract.Requires(source != null);
            Contract.Requires(target != null);
            Contract.Requires(source is PropertyInfo || source is FieldInfo);

            return source.GetPropertyOrFieldType().IsAssignableFrom(target);
        }

        /// <summary>
        /// Test if the value can be assigned to this field, property, or type.
        /// </summary>
        /// <param name="source">The field, property, or type.</param>
        /// <param name="value">The value to assign to the location. The value can be null.</param>
        /// <returns>True if the value can be assigned to the filed, property, or type.</returns>
        public static bool CanAssignValue(this MemberInfo source, object value) {
            Contract.Requires(source != null);
            Contract.Requires(source is PropertyInfo || source is FieldInfo || source is Type);

            var locationType = source as Type;

            if (locationType == null)
                locationType = source.GetPropertyOrFieldType();

            if (value == null)
                return locationType.CanBeNull();

            return locationType.IsAssignableFrom(value.GetType());
        }

        /// <summary>
        /// Return the assembly in which the MemberInfo is declared.
        /// </summary>
        /// <param name="memberInfo">The MemberInfo whose assembly will be returned.</param>
        /// <returns>Return the assembly in which the MemberInfo is declared.</returns>
        public static Assembly GetAssembly(this MemberInfo memberInfo) {
            Contract.Requires(memberInfo != null);

            return memberInfo.Module.Assembly;
        }

        /// <summary>
        /// Return the XML documentation summary for the MemberInfo. The XML documentation
        /// must be an embedded resource in the same assembly as the MemberInfo. 
        /// </summary>
        /// <param name="memberInfo">The MemberInfo for which to get documentation.</param>
        /// <returns>The XML documentation summary for the MemberInfo.</returns>
        public static string GetDocumentation(this MemberInfo memberInfo, bool inherit = false) {
            Contract.Requires(memberInfo != null);
           
            var documentation = AssemblyDocumentation.GetDocumentation(memberInfo, inherit);

            if (documentation == null || string.IsNullOrEmpty(documentation.Value))
                return null;

            return documentation.Value;
        }

        /// <summary>
        /// Return a ReflectionPointer which can be resolved to a MemberInfo. ReflectionPointer
        /// uses metadata tokens to preform the resolution. ReflectionPointers can be used stored
        /// in caches in place of MemberInfos to prevent keeping MemberInfos caches in memory.
        /// ReflectionPointers are heavier than runtime handles but lighter than the infos.
        /// </summary>
        /// <param name="memberInfo">The MemberInfo for which to create a ReflectionPointer.</param>
        /// <returns>A ReflectionPointer which can be resolved to this MemberInfo.</returns>
        public static ReflectionPointer GetReflectionPointer(this MemberInfo memberInfo) {
            Contract.Requires(memberInfo != null);
            
            return new ReflectionPointer(memberInfo);
        }

        /// <summary>
        /// If the member is a type return its base type, else if a method or property then return the 
        /// previous override in the type hierarchy.
        /// </summary>
        /// <param name="memberInfo">A type, method, or property.</param>
        /// <returns>
        /// If the member is a type return its base type, else if a method or property then return the 
        /// previous override in the type hierarchy.
        /// </returns>
        public static MemberInfo GetBaseMemberInfo(this MemberInfo memberInfo) {
            Contract.Requires(memberInfo != null);

            if (memberInfo is Type)
                return ((Type)memberInfo).BaseType;

            if (memberInfo is MethodInfo)
                return ((MethodInfo)memberInfo).GetBaseMethodInfo();

            if (memberInfo is PropertyInfo)
                return ((PropertyInfo)memberInfo).GetBasePropertyInfo();

            return null;
        }
    }

    public static partial class MethodInfoExtensions {

        public static PropertyInfo GetProperty(this MethodBase source) {
            return source.DeclaringType.Get<PropertyInfo>(source.Name.Remove(0, 4)).SingleOrDefault();
        }

        /// <summary>
        /// Returns whether the method is an override.  
        /// </summary>
        /// <param name="methodInfo">The method to test.</param>
        /// <returns>True if the method is an override, else false.</returns>
        public static bool IsOverride(this MethodInfo methodInfo) {
            Contract.Requires(methodInfo != null);

            if (!methodInfo.IsVirtual)
                return false;

            return methodInfo.GetBaseDefinition().DeclaringType != methodInfo.DeclaringType;
        }

        /// <summary>
        /// Return the MethodInfo this MethodInfo has overridden or null if the MethodInfo
        /// is the base definition or is not virtual.
        /// </summary>
        /// <param name="methodInfo">The target MethodInfo.</param>
        /// <returns>The overriden MethodInfo of this MethodInfo or null.</returns>
        public static MethodInfo GetBaseMethodInfo(this MethodInfo methodInfo) {
            Contract.Requires(methodInfo != null);

            if (!methodInfo.IsVirtual)
                return methodInfo;

            var baseType = methodInfo.DeclaringType.BaseType;
            if (baseType == null)
                return null;

            return baseType.Get<MethodInfo>(methodInfo.Name, 
                methodInfo.GetParameters().Select(o => o.ParameterType))
                .SingleOrDefault();
        }

        /// <summary>
        /// Get the poperty associated with this get or set method or null if no such
        /// property exists.
        /// </summary>
        /// <param name="getOrSetMethod">Get or set method with the name the C# compiler gives get and set methods of properties.</param>
        /// <returns>The property associated with this get or set method.</returns>
        public static PropertyInfo GetProperty(this MethodInfo getOrSetMethod) {
            Contract.Requires(getOrSetMethod != null);

            var type = getOrSetMethod.DeclaringType;
            var propertyName = Regex.Replace(getOrSetMethod.Name, "^get_|^set_", string.Empty);
            var property = type.Get<PropertyInfo>(propertyName).Single();
            return property;
        }
    }

    public static class ParameterInfoExtensions {

        /// <summary>
        /// Return the assembly in which the ParameterInfo is declared.
        /// </summary>
        /// <param name="memberInfo">The ParameterInfo whose assembly will be returned.</param>
        /// <returns>Return the assembly in which the ParameterInfo is declared.</returns>
        public static Assembly GetAssembly(this ParameterInfo parameterInfo) {
            Contract.Requires(parameterInfo != null);

            return parameterInfo.Member.Module.Assembly;
        }

        /// <summary>
        /// Return the XML documentation summary for the ParameterInfo. The XML documentation
        /// must be an embedded resource in the same assembly as the ParameterInfo. 
        /// </summary>
        /// <param name="memberInfo">The ParameterInfo for which to get documentation.</param>
        /// <returns>The XML documentation summary for the ParameterInfo.</returns>
        public static string GetDocumentation(this ParameterInfo parameterInfo, bool inherit = false) {
            Contract.Requires(parameterInfo != null);

            return AssemblyDocumentation.GetDocumentation(parameterInfo, inherit).Value;
        }

        /// <summary>
        /// Return a ReflectionPointer which can be resolved to a ParameterInfo. ReflectionPointer
        /// uses metadata tokens to preform the resolution. ReflectionPointers can be used stored
        /// in caches in place of ParameterInfos to prevent keeping MemberInfo caches in memory.
        /// </summary>
        /// <param name="parameterInfo">The ParameterInfo for which to create a ReflectionPointer.</param>
        /// <returns>A ReflectionPointer which can be resolved to this ParameterInfo.</returns>
        public static ReflectionPointer GetReflectionPointer(this ParameterInfo parameterInfo) {
            Contract.Requires(parameterInfo != null);

            return new ReflectionPointer(parameterInfo);
        }

        /// <summary>
        /// Return the ParameterInfo of the base MethodInfo in the same position or null.
        /// </summary>
        /// <param name="parameterInfo">The target ParameterInfo.</param>
        /// <returns>Return the ParameterInfo of the base MethodInfo in the same position or null.</returns>
        public static ParameterInfo GetBaseParameterInfo(this ParameterInfo parameterInfo) {
            Contract.Requires(parameterInfo != null);

            var baseMethodInfo = parameterInfo.Member as MethodInfo;
            if (baseMethodInfo == null)
                return null;

            if (parameterInfo.Position == -1)
                return baseMethodInfo.ReturnParameter;

            return baseMethodInfo.GetParameters().Skip(parameterInfo.Position).First();
        }
    }

    public static class PropertyInfoExtensions {

        public static PropertyInfo GetBasePropertyInfo(this PropertyInfo propertyInfo) {
            Contract.Requires(propertyInfo != null);

            var methodInfo = propertyInfo.GetGetMethod(true) ?? propertyInfo.GetSetMethod(true);
            var baseMethodInfo = methodInfo.GetBaseMethodInfo();
            if (baseMethodInfo == null)
                return null;

            return baseMethodInfo.GetProperty();
        }
    }

    public static partial class AssemblyExtensions {

        /// <summary>
        /// Return all reflection entities defined in this assembly that implement ICustomAttribute provider.
        /// </summary>
        /// <param name="assembly">The assembly to reflect over.</param>
        /// <returns>Return all reflection entities that implement ICustomAttribute provider.</returns>
        public static IEnumerable<ICustomAttributeProvider> GetEverything(this Assembly assembly) {
            yield return assembly;
            foreach (var cap in assembly.ManifestModule.GetEverything())
                yield return cap;
        }
    }

    public static partial class ModuleExtensions {

        /// <summary>
        /// Return all reflection entities defined in this module that implement ICustomAttribute provider.
        /// </summary>
        /// <param name="module">The module to reflect over.</param>
        /// <returns>Return all reflection entities that implement ICustomAttribute provider.</returns>
        public static IEnumerable<ICustomAttributeProvider> GetEverything(this Module module) {
            yield return module;

            var types = module.GetTypes().ToList();
            var members = types.SelectMany(o => o.Get<MemberInfo>()).ToList();
            var parameters = types.WhereIs<MethodBase>().SelectMany(o => o.GetParameters()).ToList();
            var caps = types.Cast<ICustomAttributeProvider>().Concat(members).Concat(parameters);

            foreach (var cap in caps.Where(o => o.GetAssembly().ManifestModule == module))
                yield return cap;
        }
    }
}